﻿using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.SmallBasic.Library;
using Math = System.Math;

namespace SmallBasicFun
{
    public class MyTurtle
    {
        //Display
        public static double? forceAnimateTime;
        private readonly double scale = 1;
        private string turtleShapeName;
        private bool initialized;
        private RotateTransform rotateTransform;

        //Turtle State
        private double speed = 5;
        public bool PenDown { get; set; }
        private bool visible = true;
        private double angle;
        private double x = 320.0;
        private double y = 240.0;

        public MyTurtle() : this(1)
        {
        }

        public MyTurtle(double scale)
        {
            this.scale = scale;
            InitalizeDefaults();
        }

        public double Angle
        {
            get { return angle; }
            set
            {
                angle = value;
                if (rotateTransform != null)
                {
                    GraphicsWindowHelper.Invoke(() => PreformRotation(0));
                }
            }
        }

        public double Speed
        {
            get { return speed; }
            set { speed = ForceRange(1, value, 10); }
        }

        public double X
        {
            get { return x; }
            set
            {
                x = value;
                Shapes.Move(turtleShapeName, x, y);
            }
        }

        public double Y
        {
            get { return y; }
            set
            {
                y = value;
                Shapes.Move(turtleShapeName, x, y);
            }
        }


        public Color LineColor { get; set; }

        public double LineWidth { get; set; }
        

        private void PreformRotation(double timeInMillis)
        {
            var a = new DoubleAnimation
                        {
                            To = angle,
                            Duration = createDuration(timeInMillis)
                        };
            rotateTransform.BeginAnimation(RotateTransform.AngleProperty, a);
        }

        private Duration createDuration(double timeInMillis)
        {
            return new Duration(TimeSpan.FromMilliseconds(timeInMillis));
        }

        private double ForceRange(int min, double value, int max)
        {
            return Math.Max(min, Math.Min(value, max));
        }

        private void InitalizeDefaults()
        {
            LineColor = System.Windows.Media.Colors.Black;
            LineWidth = 2;
            PenDown = true;
        }

        public void Hide()
        {
            if (visible)
            {
                GraphicsWindowHelper.VerifyAccess();
                GraphicsWindowHelper.Invoke(()=> Shapes.Remove(turtleShapeName));
                visible = false;
                initialized = false;
            }
        }

        public void Move(double distance)
        {
           
            InsureInitalized();
            double animateTime = GetAnimateTime(distance, 320.0);
            double d = (angle/180.0)*Math.PI;
            var xOld = x;
            var yOld = y;
            y = y - (distance*Math.Cos(d));
            x = x + (distance*Math.Sin(d));
           
            Shapes.Animate(turtleShapeName, x, y, animateTime);
            if (PenDown)
            {
                GraphicsWindowHelper.Invoke(()=>PreformMove(xOld, yOld, x,y, animateTime));
            }

            WaitForAnimation(animateTime);
        }

        private void PreformMove(double x1,double y1, double x2, double y2, double animateTime)
        {
            string name = ShapesHelper.GenerateNewName("_turtleLine");
            var time = createDuration(animateTime);
            var line = new Line
                            {
                                Name = name,
                                X1 = x1,
                                Y1 = y1,
                                Stroke = new SolidColorBrush(LineColor),
                                StrokeThickness = LineWidth
                            };
            GraphicsWindowHelper.AddShape(name, line);
            var xAnimate = new DoubleAnimation
                               {
                                   From = x1,
                                   To = x2,
                                   Duration = time
                               };
            var yAnimate = new DoubleAnimation
                               {
                                   From = y1,
                                   To = y2,
                                   Duration = time
                               };
            line.BeginAnimation(Line.X2Property, xAnimate);
            line.BeginAnimation(Line.Y2Property, yAnimate);
        }

        public double GetAnimateTime(double distance, double divisor)
        {
            if (forceAnimateTime != null)
            {
                return forceAnimateTime.GetValueOrDefault();
            }
            else if (speed == 10)
            {
                return 5.0;
            }
            else
            {
                return Math.Abs((distance * divisor) / (speed * speed));
            }
        }

        public void MoveTo(double x, double y)
        {
            double dX = X - x;
            double dY = Y - y;
            double d = dX*dX + dY*dY;
            if (d != 0.0)
            {
                double distance = Math.Sqrt(d);
                double num4 = (Math.Acos(dY/distance)*180.0)/Math.PI;
                if (x < X)
                {
                    num4 = 360.0 - num4;
                }
                double angle = num4 - ((Angle%360));
                if (angle > 180.0)
                {
                    angle -= 360.0;
                }
                Turn(angle);
                Move(distance);
            }
        }

        

        public void Show()
        {
            visible = true;
            InsureInitalized();
        }

        public void Turn(double angle)
        {
            InsureInitalized();
            double animateTime = GetAnimateTime(angle, 200);
            this.angle += angle;
            GraphicsWindowHelper.Invoke(() => PreformRotation(animateTime));
            WaitForAnimation(animateTime);
        }


        private void InsureInitalized()
        {
            GraphicsWindowHelper.VerifyAccess();
            if (!initialized && visible)
            {
                GraphicsWindowHelper.Invoke(SetupTurtle);
            }
            initialized = true;
        }

        private void SetupTurtle()
        {
            double size = 16.0*scale;
            var source = new BitmapImage(SmallBasicApplicationHelper.GetResourceUri("Turtle.png"));
            var turtle = new Image
                             {
                                 Source = source,
                                 Margin = new Thickness(-size/2, -size/2, 0.0, 0.0),
                                 Height = size,
                                 Width = size
                             };
            Panel.SetZIndex(turtle, 0xf4240);


            rotateTransform = new RotateTransform
                                  {
                                      Angle = angle,
                                      CenterX = size/2,
                                      CenterY = size/2
                                  };
            turtle.RenderTransform = rotateTransform;
            turtle.Width = size;
            turtle.Height = size;
            Canvas.SetLeft(turtle, x);
            Canvas.SetTop(turtle, y);
            turtleShapeName = ShapesHelper.GenerateNewName("turtle");
            if (visible)
            {
                GraphicsWindowHelper.AddShape(turtleShapeName, turtle);
            }
        }
        private void WaitForAnimation(double time)
        {
           Thread.Sleep((int) time);
        }
    }
}